package org.itboys.seller.service;

import org.apache.commons.collections.CollectionUtils;
import org.itboys.admin.entity.GlobalConfigDB;
import org.itboys.admin.service.AdminUserService;
import org.itboys.admin.service.GlobalConfigService;
import org.itboys.mongodb.utils.page.Page;
import org.itboys.seller.entity.ChangeScoreRecord;
import org.itboys.seller.entity.SiteUser;
import org.itboys.seller.mapper.AccountMapper;
import org.itboys.seller.mapper.CashFlowMapper;
import org.itboys.seller.meta.Account;
import org.itboys.seller.meta.AccountExample;
import org.itboys.seller.meta.CashFlow;
import org.itboys.seller.meta.CashFlowExample;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author hzliangchong1@corp.netease.com
 *         Date 2016/10/26.
 */
@Service
public class AccountService {

    private static final Logger logger = LoggerFactory.getLogger(AccountService.class);

    @Resource
    private AccountMapper accountMapper;

    @Resource
    private CashFlowMapper cashFlowMapper;

    @Resource
    private SellerUserService sellerUserService;

    @Resource
    private CaliforniaSiteUserService californiaSiteUserService;

    @Resource
    private AdminUserService adminUserService;

    @Resource
    private GlobalConfigService globalConfigService;

    @Resource
    private ChangeScoreRecordService changeScoreRecordService;
    @Resource
    private SubManProviderService subManProviderService;

    public Account create(long userId, int amt) {
        Account a = new Account();
        a.setRecentAmount(amt);
        a.setRecentScore(amt);
        a.setRecentScoreS(amt);
        a.setRecentScoreSs(amt);
        a.setScore(amt);
        a.setReward(0);
        a.setCreateTime(new Date());
        a.setUserId(userId);
        a.setAmount(amt);
        a.setCleanScore(0);
        a.setCleanScoreS(0);
        a.setCleanScoreSs(0);
        a.setDeltaScore(0);
        a.setUpdateTime(new Date());
        if (accountMapper.insertSelective(a) > 0) {
            return a;
        }
        return null;
    }

    /**
     * 增加余额待审核 <customer>
     *
     * @param userId
     * @param amt
     * @return
     */
    public boolean increaseToAudit(long userId, int amt) {
        CashFlow cashFlow = new CashFlow();
        cashFlow.setAmount(amt);
        cashFlow.setUserId(userId);
        cashFlow.setState(0);
        cashFlow.setCreateTime(new Date());
        cashFlowMapper.insert(cashFlow);
        return true;
    }

    @Transactional
    public boolean audit(long cashflowId, boolean pass, String reason, String auditName) {
        logger.info("audit charge start cashflowId = " + cashflowId);
        CashFlow cashFlow = cashFlowMapper.selectCashFlowForUpdate(cashflowId);
        if (cashFlow == null || cashFlow.getState() != 0) {
            return false;
        }
        SiteUser siteUser = californiaSiteUserService.getAdminUserByUserId(cashFlow.getUserId());
        if (siteUser == null || siteUser.getMemberType() != 0) {
            return false;
        }
        cashFlow.setState(pass ? 1 : 2);
        cashFlow.setReason(reason);
        cashFlow.setAuditName(auditName);
        cashFlow.setUpdateTime(new Date());
        cashFlowMapper.updateByPrimaryKeySelective(cashFlow);

        logger.info("audit charge update cashflowId = " + cashflowId);

        if (pass) {
            increase(siteUser.getSellerUserName(), siteUser.getMemberName(), siteUser.getUserId(), siteUser.getRecUserId(), cashFlow.getAmount());
        }
        return true;
    }

    private int calculateActualScore(final Account account, int maxCommision) {
        List<SiteUser> siteUserList = californiaSiteUserService.getRecUserByUserId(account.getUserId());
        List<Long> userIds = siteUserList.stream().map(SiteUser::getUserId).collect(Collectors.toList());
        // 找出下属账户
        List<Account> accountList = getByIds(userIds);
        int totalAmount = 0;
        for (Account underAccount : accountList) {
            totalAmount += underAccount.getAmount();
        }
        if (account.getAmount() <= totalAmount) {
            return account.getAmount() + totalAmount;
        }
        if (account.getAmount() <= maxCommision) {
            return account.getAmount() + totalAmount;
        }
        if (totalAmount >= maxCommision) {
            return 2 * totalAmount;
        }
        if (account.getAmount() >= maxCommision && maxCommision >= totalAmount) {
            return maxCommision + totalAmount;
        }
        return account.getAmount();
    }

    private int calculateSubManActualScore(final Account account, int amt, long fromUserId, int maxSubScore,
                                           int maxCommision) {
        List<SiteUser> siteUserList = californiaSiteUserService.getRecUserByUserId(account.getUserId());
        List<Long> userIds = siteUserList.stream().map(SiteUser::getUserId).collect(Collectors.toList());
        // 找出下属账户
        List<Account> accountList = getByIds(userIds);
        int totalAmount = 0;
        for (Account underAccount : accountList) {
            if (fromUserId == underAccount.getUserId()) {
                int subScore = subManProviderService.getScore(fromUserId, account.getUserId());
                if (subScore >= maxSubScore) {
                    return 0;
                } else {
                    int providerAmount = 0;
                    if (amt + subScore > maxSubScore) {
                        providerAmount = maxSubScore - subScore;
                    } else {
                        providerAmount = amt;
                    }
                    subManProviderService.saveProviderRecord(fromUserId, account.getUserId(), providerAmount);
                    totalAmount += subManProviderService.getScore(fromUserId, account.getUserId());
                }
            } else {
                totalAmount += subManProviderService.getScore(underAccount.getUserId(), account.getUserId());
            }
        }
        logger.info("totalAmount {}", totalAmount);
        if (account.getReward() <= totalAmount) {
            return account.getReward() + totalAmount;
        }
        if (account.getReward() <= maxCommision) {
            return account.getReward() + totalAmount;
        }
        if (totalAmount >= maxCommision) {
            return 2 * totalAmount;
        }
        if (account.getReward() >= maxCommision && maxCommision >= totalAmount) {
            return maxCommision + totalAmount;
        }
        return account.getReward();
    }

    /**
     * 增加余额 <customer>
     *
     * @param userId
     * @param amt
     * @return
     */
    @Transactional
    private boolean increase(String sellerUserName, String userName, long userId, long recUserId, int amt) {
        // validate account maybe freezom
        Account account = accountMapper.selectAccountForUpdate(userId);
        if (null == account) {
            // lazy load
            account = create(userId, 0);
        }
        // Optimistic locking
        GlobalConfigDB maxSubScore = globalConfigService.getByField("key", "maxSubScore");
        account.setAmount(account.getAmount() + amt);
        account.setReward(account.getReward() + amt); //本人本周期充值额度
        account.setRecentAmount(account.getRecentAmount() + amt);
        account.setScore(account.getScore() + amt);
        GlobalConfigDB globalConfigDB = globalConfigService.getByField("key", "maxCommision");
        int totalActualScore = calculateActualScore(account, globalConfigDB.getValue());
        logger.info("user {} get totalActualScore {}", userId, totalActualScore);
        // 当前有效积分 = 总有效积分 - 上期清空有效积分 + 偏移量，识别冠军的
        int recentActualScore = calculateSubManActualScore(account, amt, userId,
                maxSubScore.getValue(), globalConfigDB.getValue());
        if (recentActualScore > 0) {
            account.setRecentScore(recentActualScore + account.getDeltaScore());
        }
        //account.setRecentScore(totalActualScore - account.getCleanScore() + account.getDeltaScore());

        // 当前有效积分 = 总有效积分 - 上期清空有效积分 + 偏移量，识别高级冠军的
        account.setRecentScoreS(totalActualScore - account.getCleanScoreS() + account.getDeltaScore());

        // 当前有效积分 = 总有效积分 - 上期清空有效积分 + 偏移量，识别超级冠军的
        account.setRecentScoreSs(totalActualScore - account.getCleanScoreSs() + account.getDeltaScore());

        account.setUpdateTime(new Date());
        accountMapper.updateByPrimaryKeySelective(account);

        californiaSiteUserService.updateChargeTime(userId);
        ChangeScoreRecord changeScoreRecord = new ChangeScoreRecord();
        changeScoreRecord.setUserId(userId);
        changeScoreRecord.setScoreDelta(amt);
        changeScoreRecord.setResourceUserId(userId);
        changeScoreRecord.setAuditState(1);
        changeScoreRecord.setType(1);
        changeScoreRecordService.save(changeScoreRecord);
        logger.info("audit charge change score userId = " + account.getUserId());
        SiteUser rec = californiaSiteUserService.getByField("userId", recUserId);
        if (rec != null && rec.getMemberType() == 0) {
            // validate account maybe freezom
            Account recAccount = accountMapper.selectAccountForUpdate(recUserId);
            if (null == recAccount) {
                // lazy load
                recAccount = create(recUserId, 0);
            }
            // Optimistic locking
            recAccount.setScore(recAccount.getScore() + amt);
            totalActualScore = calculateActualScore(recAccount, globalConfigDB.getValue());
            // 当前有效积分 = 总有效积分 - 上期清空有效积分 + 偏移量

            recentActualScore = calculateSubManActualScore(recAccount, amt, userId,
                    maxSubScore.getValue(), globalConfigDB.getValue());
            if (recentActualScore > 0) {
                recAccount.setRecentScore(recentActualScore + recAccount.getDeltaScore());
            }
            recAccount.setRecentScoreS(totalActualScore - recAccount.getCleanScoreS() + recAccount.getDeltaScore());
            recAccount.setRecentScoreSs(totalActualScore - recAccount.getCleanScoreSs() + recAccount.getDeltaScore());
            recAccount.setUpdateTime(new Date());
            accountMapper.updateByPrimaryKeySelective(recAccount);
            changeScoreRecord = new ChangeScoreRecord();
            changeScoreRecord.setUserId(recUserId);
            changeScoreRecord.setScoreDelta(amt);
            changeScoreRecord.setResourceUserId(userId);
            changeScoreRecord.setAuditState(1);
            changeScoreRecord.setType(1);
            changeScoreRecordService.save(changeScoreRecord);
            logger.info("audit charge change score userId = " + recAccount.getUserId());
            californiaSiteUserService.updateChargeTime(recUserId);
        }
        adminUserService.increaseTotalAmount("admin", amt);
        logger.info("audit charge end");
        return true;
    }

    public void freezeByUserId(long userId, boolean freeze) {
        // validate account maybe freezom
        Account account = accountMapper.selectAccountForUpdate(userId);
        if (null == account) {
            // lazy load
            account = create(userId, 0);
        }
        // Optimistic locking
        account.setState(freeze ? 1 : 0);
        accountMapper.updateByPrimaryKeySelective(account);
    }

    public void updateScore(long userId, int scoreDelta) {
        // validate account maybe freezom
        Account account = accountMapper.selectAccountForUpdate(userId);
        if (null == account) {
            // lazy load
            account = create(userId, 0);
        }
        // Optimistic locking
        account.setDeltaScore(account.getDeltaScore() + scoreDelta);
        account.setRecentScore(account.getRecentScore() + scoreDelta);
        accountMapper.updateByPrimaryKeySelective(account);
    }

    public List<Account> getRankingUsers() {
        AccountExample example = new AccountExample();
        example.createCriteria().andStateNotEqualTo(1);
        example.setOrderByClause("-recent_score, update_time");
        example.setLimitStart(0);
        example.setLimitEnd(20);
        return accountMapper.selectByExample(example);
    }

    public List<Account> getSRankingUsers() {
        AccountExample example = new AccountExample();
        example.createCriteria().andStateNotEqualTo(1);
        example.setOrderByClause("-recent_score_s, update_time");
        return accountMapper.selectByExample(example);
    }

    public List<Account> getSSRankingUsers() {
        AccountExample example = new AccountExample();
        example.createCriteria().andStateNotEqualTo(1);
        example.setOrderByClause("-recent_score_ss, update_time");
        example.setLimitStart(0);
        example.setLimitEnd(20);
        return accountMapper.selectByExample(example);
    }

    public int getRanking(long userId) {
        return accountMapper.getRanking(userId, 0);
    }

    public Page<CashFlow> queryCashFlow(Integer state, List<Long> findUserIds, int offset, int limit) {
        CashFlowExample cashFlowExample = new CashFlowExample();
        CashFlowExample.Criteria criteria = cashFlowExample.createCriteria();
        if (state != null) {
            criteria.andStateEqualTo(state);
        }
        if (CollectionUtils.isNotEmpty(findUserIds)) {
            criteria.andUserIdIn(findUserIds);
        }
        long count = cashFlowMapper.countByExample(cashFlowExample);
        cashFlowExample.setLimitStart(offset);
        cashFlowExample.setLimitEnd(limit);
        cashFlowExample.setOrderByClause("state, update_time");
        List<CashFlow> list = cashFlowMapper.selectByExample(cashFlowExample);
        return new Page<CashFlow>(list, offset, limit, count);
    }

    public List<Account> batchQueryAccount(List<Long> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            return Collections.emptyList();
        }
        AccountExample example = new AccountExample();
        example.createCriteria().andUserIdIn(userIds);
        return accountMapper.selectByExample(example);
    }

    public List<Account> batchQueryRankAccount() {
        AccountExample example = new AccountExample();
        example.setOrderByClause("-recent_score, update_time");
        return accountMapper.selectByExample(example);
    }

    public List<Account> batchQuerySRankAccount() {
        AccountExample example = new AccountExample();
        example.setOrderByClause("-recent_score_s, update_time");
        return accountMapper.selectByExample(example);
    }

    public List<Account> batchQuerySSRankAccount() {
        AccountExample example = new AccountExample();
        example.setOrderByClause("-recent_score_ss, update_time");
        return accountMapper.selectByExample(example);
    }

    public Account getById(long uid) {
        AccountExample example = new AccountExample();
        example.createCriteria().andUserIdEqualTo(uid);
        List<Account> accounts = accountMapper.selectByExample(example);
        if (null != accounts && !accounts.isEmpty()) {
            return accounts.get(0);
        } else {
            // lazy load
            return create(uid, 0);
        }
    }

    public List<Account> getAll() {
        AccountExample example = new AccountExample();
        return accountMapper.selectByExample(example);
    }

    public List<Account> getByIds(List<Long> uids) {
        if (CollectionUtils.isEmpty(uids)) {
            return Collections.emptyList();
        }
        AccountExample example = new AccountExample();
        example.createCriteria().andUserIdIn(uids);
        return accountMapper.selectByExample(example);
    }

    public boolean emptyScoreAfterShareOutBonus(List<Long> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            return false;
        }
        return accountMapper.emptyScoreAfterShareOutBonus(userIds) > 0;
    }

    public boolean emptySScoreAfterShareOutBonus(List<Long> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            return false;
        }
        return accountMapper.emptyScoreSAfterShareOutBonus(userIds) > 0;
    }

    public boolean emptySSScoreAfterShareOutBonus(List<Long> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            return false;
        }
        return accountMapper.emptyScoreSSAfterShareOutBonus(userIds) > 0;
    }

    public int getRecentTotalAmount() {
        return accountMapper.getRecentTotalAmount();
    }

    public boolean emptyRecentAmount() {
        return accountMapper.emptyRecentAmount() > 0;
    }
}
